package com.zjxf.shiro.config;

import com.zjxf.common.ShiroConst;
import com.zjxf.config.MyShiroFilterFactoryBean;
import com.zjxf.properties.RedisProperties;
import com.zjxf.realm.MyShiroRealm;
import com.zjxf.session.RedisSessionDao;
import com.zjxf.shiro.session.WeChatSessionManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * created with IntelliJ IDEA
 *
 * @author: create by limu
 * Date: 2020/6/20
 * Time：10:07
 */
@Slf4j
@Configuration
public class ShiroConfig {

    @Resource
    private RedisProperties redisProperties;

    /**
     * shiro过滤器链
     *
     * @param securityManager 认证管理器
     * @return ShiroFilterFactoryBean
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new MyShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setLoginUrl("/login");
        factoryBean.setSuccessUrl("/index");
        factoryBean.setUnauthorizedUrl("/404");

        LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
        factoryBean.setFilters(filtersMap);
        loadShiroFilterChain(factoryBean);
        return factoryBean;
    }

    /**
     * 配置shiro拦截器链条
     *
     * @param factoryBean 拦截器工厂
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean factoryBean) {
        Map<String, String> filterChainMap = new LinkedHashMap<>();
        filterChainMap.put("/swagger-ui.html/**", "anon");
        filterChainMap.put("/swagger-resources/**", "anon");
        filterChainMap.put("/app/home/**", "anon");
        filterChainMap.put("/app/collection/**","anon");
        filterChainMap.put("/app/banner/**","anon");
        filterChainMap.put("/app/landmark/**","anon");
        filterChainMap.put("/app/lmhome/**","anon");
        filterChainMap.put("/app/course/**","anon");
        filterChainMap.put("/v2/api-docs", "anon");
        filterChainMap.put("/webjars/springfox-swagger-ui/**", "anon");

        filterChainMap.put("/static/**", "anon");   //静态资源
        filterChainMap.put("/favicon.ico", "anon"); //页面小标

        filterChainMap.put("/admin/login", "anon");    //管理员登录页面
        filterChainMap.put("/admin/addOrUpdate", "anon");    //管理员注册账号页面
        filterChainMap.put("/seller/login", "anon");    //商家登录页面
        filterChainMap.put("/seller/addOrUpdate", "anon");    //商家注册账号页面

        filterChainMap.put("/index", "anon");    //注册账号页面

        filterChainMap.put("/**", "authc");

        factoryBean.setFilterChainDefinitionMap(filterChainMap);
    }

    /**
     * shiro集成redis缓存管理
     *
     * @return RedisManager
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(redisProperties.getHost());
        redisManager.setPort(redisProperties.getPort());
        redisManager.setPassword(redisProperties.getPassword());
        redisManager.setTimeout(redisProperties.getTimeOut());
        return redisManager;
    }

    /**
     * session 会话验证调度器
     * 设置shiro失效session的扫描时间
     *
     * @return ExecutorServiceSessionValidationScheduler
     */
    @Bean(name = "sessionValidationScheduler")
    public ExecutorServiceSessionValidationScheduler executorServiceSessionValidationScheduler() {
        ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
        scheduler.setInterval(ShiroConst.SHIRO_SCAN_LONE_SESSION);
        scheduler.setThreadNamePrefix(ShiroConst.SHIRO_LONE_SESSION_NAME);
        return scheduler;
    }

    /**
     * 配置session保存的id
     *
     * @return SimpleCookie
     */
    @Bean(name = "sessionIdCookie")
    public SimpleCookie sessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(ShiroConst.SHIRO_SESSION_COOKIE_NAME);
        simpleCookie.setHttpOnly(Boolean.TRUE);
        simpleCookie.setMaxAge(ShiroConst.SHIRO_SESSION_COOKIE_MAX_AGE);
        return simpleCookie;
    }

    /**
     * session管理
     *
     * @return WeChatSessionManager
     */
    @Bean(name = "sessionManager")
    public WeChatSessionManager getSessionManage() {
        WeChatSessionManager sessionManager = new WeChatSessionManager();
        sessionManager.setCacheManager(cacheManager());//Shiro的缓存管理
        sessionManager.setSessionIdCookieEnabled(Boolean.TRUE); //是否禁用会话id
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionDAO(redisSessionDao());
        sessionManager.setDeleteInvalidSessions(Boolean.TRUE);
        sessionManager.setGlobalSessionTimeout(ShiroConst.SHIRO_SESSION_SESSION_MAX_AGE);
        sessionManager.setSessionValidationSchedulerEnabled(Boolean.TRUE);
        sessionManager.setSessionValidationScheduler(executorServiceSessionValidationScheduler());
        sessionManager.setSessionIdUrlRewritingEnabled(Boolean.FALSE);
        return sessionManager;
    }

    /**
     * 自定义realm
     *
     * @return MyShiroRealm
     */
    @Bean(name = "shiroRealm")
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setAuthenticationCachingEnabled(Boolean.FALSE);
        myShiroRealm.setAuthorizationCachingEnabled(Boolean.FALSE);
        return myShiroRealm;
    }

    /**
     * cacheManager 缓存 redis实现
     *
     * @return RedisCacheManager
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * 认证管理
     *
     * @return DefaultWebSecurityManager
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(cacheManager());
        securityManager.setSessionManager(getSessionManage());
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    /**
     * 注解访问授权动态拦截，不然不会执行doGetAuthenticationInfo
     *
     * @param defaultWebSecurityManager 认证管理器
     * @return AuthorizationAttributeSourceAdvisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactoryBean.setArguments(defaultWebSecurityManager());
        return methodInvokingFactoryBean;
    }

    /**
     * 启用授权注解拦截方式
     *
     * @return AuthorizationAttributeSourceAdvisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 注册sessionDao
     *
     * @return sessionDao
     */
    @Bean
    public RedisSessionDao redisSessionDao() {
        return new RedisSessionDao();
    }
}
